
package com.sm.shoplook.interceptor;

import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import com.sm.shoplook.constant.ResultMessage;
import com.sm.shoplook.vo.base.ApiException;
import com.sm.shoplook.vo.base.ApiResult;

/**
 * 异常统一处理类
 * @author chenghb-799
 * @date 2017年8月2日下午5:41:15
 */
@ControllerAdvice
public class ValidExceptionResolver {

	private final static MappingJackson2JsonView RESPONSEVIEW = new MappingJackson2JsonView();
	private static final String RESULT = "result";
	private static final String MESSAGE = "msg";
	private static final String TYPE_MISMATCH = "typeMismatch";
	private static final String IN = "in";
	private static final Logger LOGGER = LoggerFactory.getLogger(ValidExceptionResolver.class);

	/**
	 * @函数功能 入参的类型错误处理
	 * @创建时间 2016-11-17 上午10:55:01 @author bb.h
	 * @param exception
	 * @return
	 */
	private static ApiResult doParamsTypeException(MethodArgumentTypeMismatchException exception) {
		// 入参的类型错误
		ModelMap model = new ModelMap();
		model.addAttribute(RESULT, 4002);
		return new ApiResult(4002, "参数类型错误[" + exception.getRequiredType().getSimpleName() + "@" + exception.getName()
				+ ":" + exception.getValue() + "] ");
	}

	/**
	 * 参数异常
	 * @date 2017年7月4日下午6:19:14
	 * @author liuxinyu-798
	 * @since 1.0.0
	 * @param exception
	 * @return
	 */
	private static ApiResult doParamsException(MissingServletRequestParameterException exception) {
		return new ApiResult(4002, "参数错误[" + exception.getParameterType() + "@" + exception.getParameterName() + "]");
	}

	/**
	 * @函数功能 请求方法找不到错误处理
	 * @创建时间 2016-11-17 上午10:51:40 @author bb.h
	 * @param exception
	 * @return
	 */
	private static ApiResult doAddressException(HttpRequestMethodNotSupportedException exception) {
		StringBuilder builder = new StringBuilder("[");
		for (String needMethodName : exception.getSupportedMethods()) {
			builder.append(needMethodName + ",");
		}
		builder.append("]");
		return new ApiResult(4002, "请求方式错误 [" + exception.getMethod() + "]此接口支持以下模式 " + builder);
	}

	/**
	 * @函数功能 spring验证规则错误处理
	 * @创建时间 2016-11-17 上午10:47:40 @author bb.h
	 * @param exception
	 * @return
	 */
	private static ApiResult doBindException(BindException exception) {
		// 1.组织错误提示信息
		StringBuilder result = new StringBuilder("");
		ObjectError objectError = exception.getAllErrors().get(0);
		if (TYPE_MISMATCH.equals(objectError.getCode())) {
			FieldError fieldError = (FieldError) objectError;
			String type = null;
			if (fieldError.getCodes().length > 1) {
				type = fieldError.getCodes()[2];
				int index = type.lastIndexOf(".") + 1;
				if (index < type.length()) {
					type = type.substring(index);
				} else {
					type = "未知类型";
				}
			}
			result.append("参数类型错误[" + type + "@" + fieldError.getField() + ":" + fieldError.getRejectedValue() + "]");
		} else if (IN.equals(objectError.getCode())) {
			FieldError fieldError = (FieldError) objectError;
			result.append("存在无效的参数值: " + fieldError.getField() + "=" + fieldError.getRejectedValue());
		} else {
			result.append(objectError.getDefaultMessage());
		}
		LOGGER.error("result", result.toString());
		// 2.组织返回响应信息
		String msg = result.toString();
		return new ApiResult(4002, msg);
	}

	@ExceptionHandler({ Exception.class })
	@ResponseBody
	public ApiResult resolveException(Exception ex) {
	    LOGGER.error("e: {}", ex);
		// 参数验证错误
		if (ex instanceof BindException) {
			return doBindException((BindException) ex);
		} else if (ex instanceof HttpRequestMethodNotSupportedException) {
			return doAddressException((HttpRequestMethodNotSupportedException) ex);
		} else if (ex instanceof MethodArgumentTypeMismatchException) {
			return doParamsTypeException((MethodArgumentTypeMismatchException) ex);
		} else if (ex instanceof MissingServletRequestParameterException) {
			return doParamsException((MissingServletRequestParameterException) ex);
		} else if (ex instanceof ApiException) {
			return doApiException((ApiException) ex);
		} else if (ex instanceof MethodArgumentNotValidException) {
			return doParamNotValidException((MethodArgumentNotValidException) ex);
		} else if (ex instanceof HttpMessageConversionException) {
			return doParamConvertException((HttpMessageConversionException) ex);
		} else if (ex instanceof UnauthorizedException) {
			return new ApiResult(ResultMessage.NO_PERMISSION_1003);
		}
		return doException(ex);
	}

	/**
	 * 验证参数
	 * @date 2017/7/12 10:56
	 * @author xuph-1028
	 * @param exception
	 * @return
	 */
	private static ApiResult doParamNotValidException(MethodArgumentNotValidException exception) {
		return new ApiResult(4002, exception.getBindingResult().getFieldError().getField()
				+ exception.getBindingResult().getFieldError().getDefaultMessage());
	}

	/**
	 * 验证参数匹配
	 * @date 2017/7/12 11:26
	 * @author xuph-1028
	 * @param exception
	 * @return
	 */
	private static ApiResult doParamConvertException(HttpMessageConversionException exception) {
		return new ApiResult(11003, "参数转换异常");
	}

	/**
	 * 处理ApiException
	 * @date 2017年6月22日下午4:42:25
	 * @author liuxinyu-798
	 * @since 1.0.0
	 * @param ex APIException
	 * @return
	 */
	public static ApiResult doApiException(ApiException ex) {
		return new ApiResult(ResultMessage.RESULT_ERROR_STATUS_0, ex.getMessage());
	}

	/**
	 * 其他异常捕获
	 * @date 2017年6月22日下午4:47:32
	 * @author liuxinyu-798
	 * @since 1.0.0
	 * @param ex Exception
	 * @return
	 */
	public static ApiResult doException(Exception ex) {
		return new ApiResult(ResultMessage.RESULT_ERROR_0);
	}
}
